//==============================================================================
#include "Socket.h"

using namespace net;
//==============================================================================
net::Socket::Socket(void)
{
}
//------------------------------------------------------------------------------
net::Socket::~Socket(void)
{
}
//------------------------------------------------------------------------------
void Socket::setSocketConnection(SOCKET socket)
{
	this->socketConnection = socket;	
}
//------------------------------------------------------------------------------
SOCKET Socket::getSocketConnection()
{
	return this->socketConnection;
}
//------------------------------------------------------------------------------
int Socket::doSend(const char* sendbuf) throw (NetworkException)
{
	int sendResult = send(socketConnection, sendbuf, (int)strlen(sendbuf), 0);
	if (sendResult == SOCKET_ERROR)
	{
		Log::getInstance()->error("[SOCKET] doSend() failed! ; (ErrorCode: " + Log::getInstance()->intToString(WSAGetLastError()) + " )");
		throw NetworkException(WSAGetLastError());
	}
	return sendResult;
}
//------------------------------------------------------------------------------
int Socket::doReceive(char* recvbuf, int recvbuflen) throw (NetworkException)
{
	int recieveResult = recv(socketConnection, recvbuf, recvbuflen, 0);
	if (recieveResult == SOCKET_ERROR)
		{
			Log::getInstance()->error("[SOCKET] doReceive() failed! ; (ErrorCode: " + Log::getInstance()->intToString(WSAGetLastError()) + " )");
			throw NetworkException(WSAGetLastError());
		}
	return recieveResult;
}
//------------------------------------------------------------------------------
void Socket::doSendComplete(ByteBuffer* sendbuf) throw (NetworkException)
{
	int totalBytesToSend = sizeof(int);
	int bytesSend = 0;
	int sendResult;
	int buffSize = sendbuf->size();
	const char* data = (const char*)&buffSize;
	
	while (bytesSend < totalBytesToSend)
	{
		sendResult = send(socketConnection, (data+bytesSend), totalBytesToSend, 0);
		if (sendResult == SOCKET_ERROR)
		{
			Log::getInstance()->error("[SOCKET] doSendComplete() failed! ; (ErrorCode: " + Log::getInstance()->intToString(WSAGetLastError()) + " )");
			bytesSend = -1;
			throw NetworkException(WSAGetLastError());
		}
		else
		{
			bytesSend += sendResult;
			totalBytesToSend -= sendResult;
		}
	}

	totalBytesToSend = buffSize;
	data = sendbuf->get();
	bytesSend = 0;

	while (bytesSend < totalBytesToSend)
	{
		sendResult = send(socketConnection, (data+bytesSend), totalBytesToSend, 0);
		if (sendResult == SOCKET_ERROR)
		{
			Log::getInstance()->error("[SOCKET] doSendComplete() failed! ; (ErrorCode: " + Log::getInstance()->intToString(WSAGetLastError()) + " )");
			bytesSend = -1;
			throw NetworkException(WSAGetLastError());
		}
		else
		{
			bytesSend += sendResult;
			totalBytesToSend -= sendResult;
		}
	}
}
//------------------------------------------------------------------------------
void Socket::doReceiveComplete(ByteBuffer* recvbuf) throw (NetworkException)
{
	recvbuf->clear();
	int totalBytesToRecieve = sizeof(int);
	int bytesRecieved = 0;
	int receiveResult = 0;
	char recievebufferData[1024];
	int messageLengh;
	char* data = (char*)&messageLengh;

	while (bytesRecieved < totalBytesToRecieve)
	{
		receiveResult = recv(socketConnection, (data+receiveResult) ,totalBytesToRecieve, 0);
		if (receiveResult == SOCKET_ERROR)
		{
			Log::getInstance()->error("[SOCKET] doReceiveComplete() failed! ; (ErrorCode: " + Log::getInstance()->intToString(WSAGetLastError()) + " )");
			bytesRecieved = -1;
			throw NetworkException(WSAGetLastError());
		}
		else
		{
			bytesRecieved += receiveResult;
			totalBytesToRecieve -= receiveResult;
		}
	}
		
	totalBytesToRecieve = messageLengh;
	bytesRecieved = 0;
	
	while (bytesRecieved < totalBytesToRecieve)
	{
		receiveResult = recv(socketConnection, recievebufferData,1024, 0);
		if (receiveResult == SOCKET_ERROR)
		{
			Log::getInstance()->error("[SOCKET] doReceiveComplete() failed! ; (ErrorCode: " + Log::getInstance()->intToString(WSAGetLastError()) + " )");
			bytesRecieved = -1;
			throw NetworkException(WSAGetLastError());
		}
		else
		{
			recvbuf->put(recievebufferData,receiveResult);
			bytesRecieved += receiveResult;
			totalBytesToRecieve -= receiveResult;
		}
	}
}
//------------------------------------------------------------------------------
void Socket::doClose(void)
{
	int closeResult = closesocket(socketConnection);
	if (closeResult == SOCKET_ERROR)
    {
		Log::getInstance()->error("[SOCKET] doClose() failed! ; (ErrorCode: " + Log::getInstance()->intToString(WSAGetLastError()) + " )");
		throw NetworkException(WSAGetLastError());
    }
}
//------------------------------------------------------------------------------
void Socket::doShutdown(void) throw (NetworkException)
{
	int disconnectResult = shutdown(socketConnection, SD_BOTH);
    if (disconnectResult == SOCKET_ERROR)
    {
		Log::getInstance()->error("[SOCKET] doShutdown() failed! ; (ErrorCode: " + Log::getInstance()->intToString(WSAGetLastError()) + " )");
		throw NetworkException(WSAGetLastError());
    }
}
//------------------------------------------------------------------------------
string Socket::toString(void)
{
	int i = 1;
	struct addrinfo* ptr = NULL;
	string info = "";
	stringstream ss;
	for(ptr=obtainedAddress; ptr != NULL; ptr=ptr->ai_next)
	{
		ss << ptr->ai_flags;
		ss.str();
		info.append("[Flags= ").append(ss.str());
		ss.clear();
        info.append("|Family= ");
        switch (ptr->ai_family)
		{
            case AF_UNSPEC:
                info.append("Unspecified");
                break;
            case AF_INET:
                info.append("AF_INET (IPv4)");
                break;
            case AF_INET6:
                info.append("AF_INET6 (IPv6)");
                break;
            case AF_NETBIOS:
                info.append("AF_NETBIOS (NetBIOS)");
                break;
            default:
				ss << ptr->ai_family;
				info.append("Other(").append(ss.str()).append(")");
				ss.clear();
                break;
        }
        info.append("|Socket type=");
        switch (ptr->ai_socktype) {
            case 0:
                info.append("Unspecified");
                break;
            case SOCK_STREAM:
                info.append("SOCK_STREAM (stream)");
                break;
            case SOCK_DGRAM:
                info.append("SOCK_DGRAM (datagram)");
                break;
            case SOCK_RAW:
                info.append("SOCK_RAW (raw)");
                break;
            case SOCK_RDM:
                info.append("SOCK_RDM (reliable message datagram)");
                break;
            case SOCK_SEQPACKET:
                info.append("SOCK_SEQPACKET (pseudo-stream packet)");
                break;
            default:
				ss << ptr->ai_socktype;
				info.append("Other(").append(ss.str()).append(")");
				ss.clear();
                break;
        }
        info.append("|Protocol=");
        switch (ptr->ai_protocol) {
            case 0:
                info.append("Unspecified");
                break;
            case IPPROTO_TCP:
                info.append("IPPROTO_TCP (TCP)");
                break;
            case IPPROTO_UDP:
                info.append("IPPROTO_UDP (UDP)");
                break;
            default:
				ss << ptr->ai_protocol;
				info.append("Other(").append(ss.str()).append(")");
				ss.clear();
                break;
        }
		ss << ptr->ai_addrlen;
		info.append("|sockaddr length=").append(ss.str());
		ss.clear();
		if (ptr->ai_canonname != 0)
		{
			info.append("|Canonical name= ").append(ptr->ai_canonname);
		}
		info.append("]");
    }
	return info;
}